/**
 * @(#)FlowChain.java, 十二月 15, 2024.
 * <p>
 * Copyright 2024 memberclub.com. All rights reserved.
 * memberclub.COM PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.example.design.flow.chain;

import com.example.design.flow.node.EmptySubFlowNode;
import com.example.design.flow.node.FlowNode;
import com.example.design.flow.node.SubFlowNode;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.SneakyThrows;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 * 流程链，包含多个FlowNode节点，负责管理节点的添加和执行。提供了添加节点的方法，包括普通节点和带有子流程的节点。
 *
 * @param <T>
 */
@Data
public class FlowChain<T> {

    public List<FlowNode<T>> nodes = Lists.newArrayList();

    public List<Class<? extends FlowNode<T>>> clazzs = Lists.newArrayList();

    public FlowChainManager flowChainManager;

    private Class<T> contextClass;


    public static <T> FlowChain<T> newChain(FlowChainManager flowChainManager, Class<T> clazz) {
        FlowChain<T> chain = new FlowChain<>();
        chain.setContextClass(clazz);
        chain.setFlowChainManager(flowChainManager);
        return chain;
    }


    public void execute(T context) {
        flowChainManager.execute(this, context);
    }


    public FlowChain<T> addNode(Class<? extends FlowNode<T>> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        clazzs.add(clazz);
        FlowNode<T> bean = clazz.getDeclaredConstructor().newInstance();
        nodes.add(bean);
        return this;
    }

    public FlowChain<T> addNode(FlowNode<T> bean) {
        clazzs.add((Class<? extends FlowNode<T>>) bean.getClass());
        nodes.add(bean);
        return this;
    }


    @SneakyThrows
    public <S> FlowChain<T> addEmptyNodeWithSubNodes(Class<? extends EmptySubFlowNode<?>> clazz,
                                                     Class<T> subContextClass,
                                                     List<Class<? extends FlowNode<T>>> subNodes) {
        SubFlowNode<T, T> bean = (SubFlowNode<T, T>) clazz.newInstance();
        FlowChain<T> subChain = newChain(flowChainManager, subContextClass);
        for (Class<? extends FlowNode<T>> subNode : subNodes) {
            subChain.addNode(subNode);
        }
        bean.setSubChain(subChain);
        nodes.add(bean);
        return this;
    }

    public <S> FlowChain<T> addNodeWithSubNodes(Class<? extends SubFlowNode<T, S>> clazz,
                                                Class<S> subContextClass,
                                                List<Class<? extends FlowNode<S>>> subNodes) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        SubFlowNode<T, S> bean = clazz.getDeclaredConstructor().newInstance();
        FlowChain<S> subChain = newChain(flowChainManager, subContextClass);
        for (Class<? extends FlowNode<S>> subNode : subNodes) {
            subChain.addNode(subNode);
        }
        bean.setSubChain(subChain);
        nodes.add(bean);
        return this;
    }

    public <S> FlowChain<T> addNodeWithSubNodes(Class<? extends SubFlowNode<T, S>> clazz,
                                                FlowChain<S> subChain) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        SubFlowNode<T, S> bean = clazz.getDeclaredConstructor().newInstance();
        bean.setSubChain(subChain);
        nodes.add(bean);
        return this;
    }

}